import requests  # `pip install requests` [doc](http://docs.python-requests.org/zh_CN/latest/)
from urllib.parse import urlparse
from tools import convert
from tools.auto_property import prop


class Response(object):
    headers = prop("headers", bind_field="_headers", readonly=True)
    request = prop("request", bind_field="_request", readonly=True)
    status_code = prop("status_code", bind_field="_status_code", readonly=True)
    ok = prop("ok", bind_field="_ok", readonly=True)
    exception = prop("exception", bind_field="_exception", readonly=True)
    raw = prop("raw", bind_field="_raw", readonly=True)
    json = prop("json", readonly=True)
    encoding = prop("encoding", bind_field="_encoding", readonly=True)
    cookies = prop("cookies", bind_field="_cookies", readonly=True)
    real_object = prop("cookies", bind_field="_real_object", readonly=True)
    text = prop("text", bind_field="_text", readonly=True)

    def __init__(self, response, req):
        if not isinstance(req, Request):
            raise TypeError('parameter: "req" value is invalid (must <Request>)')
        self._request = req
        if isinstance(response, BaseException):
            self._exception = response
            self._ok = False
            return

        if not isinstance(response, requests.Response):
            raise TypeError('parameter: "response" value is invalid (must <requests.Response>)')

        self._real_object = response
        # 响应头
        self._headers = response.headers
        self._headers.content_encoding = response.headers.get("content-encoding")
        self._headers.content_type = response.headers.get("content-type")
        self._headers.server = response.headers.get("server")
        self._headers.etag = response.headers.get("etag")
        self._headers.keep_alive = bool((response.headers.get('connection') or "").lower() == 'keep-alive')

        self._raw = response.content
        self._text = response.text
        self._status_code = response.status_code
        self._ok = response.ok
        self._cookies = response.cookies
        self._encoding = response.encoding

    def _get_json(self):
        return convert.to_json_obj(self._text)


class Request(object):
    timeout = prop("timeout", bind_field="_timeout", default=15, check_type=(int, float))
    auto_redirect = prop("auto_redirect", bind_field="_auto_redirect", default=False, check_type=bool)
    method = prop("method", bind_field="_method", default="GET", check_type=str)
    url = prop("url", bind_field="_url", check_type=str)
    headers = prop("headers", bind_field="_headers")
    query = prop("query", bind_field="_query")
    body = prop("body", bind_field="_body")
    path_params = prop("path_params", bind_field="_path_params")
    cert = prop("cert", bind_field="_cert")
    proxies = prop("proxies", bind_field="_proxies")
    user_agent = prop("user_agent", check_type=str)

    def __init__(self, method=None, url=None):
        self._headers = {}
        if url is None:
            url = method
            method = None

        if method is not None:
            self.method = method
        if url is not None:
            self.url = url

    def _set_timeout(self, value):
        self._timeout = int(value)

    def _set_method(self, value):
        self._method = value.upper()

    def _set_url(self, value):
        value = value.strip()
        if not value.startswith("//") and not value.find("://"):
            value = "//" + value
        self._url = urlparse(value)  # (scheme='http', netloc='', path='', params='', query='', fragment='')

    def _set_headers(self, value):
        d = convert.to_dict(value)
        if isinstance(d, (list, tuple, set)):
            d = dict(d)
        if not isinstance(d, dict):
            raise ValueError('property: "headers" value is `%s` and invalid ' % str(value))
        self._headers = d

    def _set_proxies(self, value):
        d = convert.to_dict(value)
        if isinstance(d, (list, tuple, set)):
            d = dict(d)
        if not isinstance(d, dict):
            raise ValueError('property: "proxies" value is `%s` and invalid ' % str(value))
        self._proxies = d

    def _set_query(self, value):
        self._query = value if isinstance(value, str) or value is None else convert.to_dict(value)

    def _set_path_params(self, value):
        self._path_params = convert.to_dict(value)

    def _get_user_agent(self):
        return self.headers["User-Agent"]

    def _set_user_agent(self, value):
        self.headers["User-Agent"] = value

    def send(self, json=True):
        url = self.url
        args = {
            "allow_redirects": self.auto_redirect,
            "timeout": self.timeout
        }
        if self.headers:
            args["headers"] = self.headers
        if self.query:
            args["params"] = self.query
        if self.body:
            if json:
                args["json"] = self.body
            else:
                args["data"] = self.body
        path = url.path
        if self.path_params is not None:
            for key, value in self.path_params:
                path = path.replace("{%s}" % key, str(value))
        try:
            with requests.request(self.method, "%s://%s%s" % (url.scheme or "http", url.netloc, path), **args) as r:
                return Response(r, self)
        except BaseException as ex:
            return Response(ex, self)


# 测试代码
if __name__ == "__main__":
    request = Request()
    request.method = "post"  # 调用 _set_method
    print(vars(request))
    print(request.method)  # 输出 POST
    request.timeout = 1.99  # 调用 _set_timeout
    print(request.timeout)  # 输出 1
    # request.auto_redirect = "False"  # 验证类型 check_type=bool 不通过, 抛出异常

    request = Request("http://fcweb.oss-cn-hangzhou.aliyuncs.com/{name}/test.json")
    request.path_params = {"name": "TEST"}
    print(request.method)
    r = request.send()
    print(r.text)
    print(r.json)

    assert r.text == '{"id":1,"name":"blqw"}'
    assert r.json.id == 1
    assert r.json.name == "blqw"
